package query

import (
	"fmt"
	"shqsoft.cn/pasnj/bi/cube"
	"shqsoft.cn/pasnj/bi/database"
	"strconv"
	"strings"
)

//Index 指标结构
type Index struct {
	id         int
	name       string
	typeID     int
	typeName   string
	aggregator string
	txid       int
	txnm       string
	context    *IndexContext
	calcExp    *CalcExp
}

//IndexContext 指标上下文
type IndexContext struct {
	cube    string
	measure string
	dimKeys map[string][]string
}

//CalcExp 计算表达式
type CalcExp struct {
	method string
	exps   []string
}

var indexMap map[string]*Index

func buildIndex() error {
	sqlStr := "select INID,INNM,TPID,TPNM,TXID,TXNM,CONTEXT,AGG from dim_index"
	dl := database.DefaultDataLoader
	rows, err := dl.LoadDataBySQL(sqlStr)
	if err != nil {
		return err
	}
	defer rows.Close()
	indexMap = make(map[string]*Index)
	for rows.Next() {
		var id, typeID, txid int
		var name, typeName, txnm string
		var context, agg *string
		err := rows.Scan(&id, &name, &typeID, &typeName, &txid, &txnm, &context, &agg)
		if err != nil {
			return err
		}
		ind := &Index{
			id:       id,
			name:     name,
			typeID:   typeID,
			typeName: typeName,
			txid:     txid,
			txnm:     txnm,
		}
		if context != nil {
			if strings.HasPrefix(*context, "#") {
				calces := *context
				start := strings.Index(calces, "(")
				end := strings.LastIndex(calces, ")")
				expEytes := []byte(calces)
				method := string(expEytes[1:start])
				exp := string(expEytes[start+1 : end])
				exps, err := parseExp(exp)
				if err != nil {
					return err
				}
				ind.calcExp = &CalcExp{
					method: method,
					exps:   exps,
				}
			} else {
				ind.context, err = parseSet(*context)
				if err != nil {
					return err
				}
			}
		}
		if agg != nil {
			ind.aggregator = *agg
		}
		indexMap[strconv.Itoa(id)] = ind
	}
	return nil
}
func parseExp(exp string) ([]string, error) {
	exp = strings.TrimSpace(exp)
	exps := make([]string, 0)
	bytesExp := []byte(exp)
	startIndex := 0
	nextChar := ","
	for i := 0; i < len(bytesExp); i++ {
		c := string(bytesExp[i : i+1])
		if c == "(" {
			nextChar = ")"
		} else if c == "{" {
			nextChar = "}"
		} else if c == nextChar {
			if startIndex != -1 {
				if c == ")" || c == "}" {
					exps = append(exps, string(bytesExp[startIndex:i+1]))
					startIndex = -1
					nextChar = ","
				} else if c == "," {
					exps = append(exps, string(bytesExp[startIndex:i]))
				}
			} else {
				startIndex = i + 1
			}
		} else {
			if startIndex == -1 {
				if strings.TrimSpace(c) != "" {
					return nil, fmt.Errorf("指标表达式%s错误,在%d位置有错误！", exp, i+1)
				}
			}
		}
	}
	return exps, nil
}

func parseSet(setExp string) (*IndexContext, error) {
	setExp = strings.TrimSpace(setExp)
	ic := new(IndexContext)
	if strings.HasPrefix(setExp, "{") {
		if !strings.HasSuffix(setExp, "}") {
			return nil, fmt.Errorf("指标表达式%s错误,集合以'{'后头也必须以'}'结尾", setExp)
		}
		setExp = strings.TrimLeft(setExp, "{")
		setExp = strings.TrimRight(setExp, "}")
		tups, err := parseExp(setExp)
		if err != nil {
			return nil, err
		}
		ic.dimKeys = make(map[string][]string)
		dimValMap := make(map[string][]string)
		for _, tup := range tups {
			cube, measure, dimkeys, dimVals, err := parseTuple(tup)
			if err != nil {
				return nil, err
			}
			if ic.cube == "" {
				ic.cube = cube
				ic.measure = measure
				if cube == "" || measure == "" {
					return nil, fmt.Errorf("元组%s中的度量表达式错误", tup)
				}
			} else {
				if cube != ic.cube || measure != ic.measure {
					return nil, fmt.Errorf("集合表达式%s错误,度量值必须为同一cube中的同一个measure", setExp)
				}
			}
			if len(ic.dimKeys) == 0 {
				ic.dimKeys = dimkeys
			} else {
				for k, v := range dimkeys {
					if _, ok := ic.dimKeys[k]; ok {
						exist := false
						if _, ok2 := dimValMap[k]; ok2 {
							for _, v2 := range dimValMap[k] {
								if v2 == dimVals[k] {
									exist = true
									break
								}
							}
						}
						if !exist {
							ic.dimKeys[k] = append(ic.dimKeys[k], v...)
						}
					} else {
						return nil, fmt.Errorf("集合表达式%s错误,每个元组中的维度不一", setExp)
					}
				}
			}
			for k, v := range dimVals {
				if _, ok := dimValMap[k]; !ok {
					dimValMap[k] = make([]string, 0)
				}
				dimValMap[k] = append(dimValMap[k], v)
			}
		}
	} else {
		cube, measure, dimkeys, _, err := parseTuple(setExp)
		if err != nil {
			return nil, err
		}
		ic.cube = cube
		ic.measure = measure
		ic.dimKeys = dimkeys
	}
	return ic, nil
}
func parseTuple(tupExp string) (string, string, map[string][]string, map[string]string, error) {
	tupExp = strings.TrimSpace(tupExp)
	dimKeys := make(map[string][]string)
	dimVals := make(map[string]string)
	var cube, measure string
	if strings.HasPrefix(tupExp, "(") {
		if !strings.HasSuffix(tupExp, ")") {
			return "", "", nil, nil, fmt.Errorf("元组表达式%s错误,元组以'('后头也必须以')'结尾", tupExp)
		}
		tupExp = strings.TrimLeft(tupExp, "(")
		tupExp = strings.TrimRight(tupExp, ")")
		mems := strings.Split(tupExp, ",")
		for _, mem := range mems {
			if strings.HasPrefix(mem, "measures.") {
				meas := strings.Split(mem, ".")
				cube = meas[1]
				measure = meas[2]
				continue
			}
			meas := strings.Split(mem, ".")
			if len(meas) < 3 {
				return "", "", nil, nil, fmt.Errorf("成员表达式%s错误", mem)
			}
			dim, keys, err := parseMember(mem, meas[3])
			if err != nil {
				return "", "", nil, nil, err
			}
			if _, ok := dimKeys[dim]; ok {
				return "", "", nil, nil, fmt.Errorf("元组表达式%s错误,元组中的成员不能是同一维度", tupExp)
			}
			dimKeys[dim] = keys
			dimVals[dim] = meas[3]
		}
	} else {
		if strings.HasPrefix(tupExp, "measures.") {
			meas := strings.Split(tupExp, ".")
			if len(meas) != 3 {
				return "", "", nil, nil, fmt.Errorf("度量值表达式%s错误", tupExp)
			}
			cube = meas[1]
			measure = meas[2]
		} else {
			return "", "", nil, nil, fmt.Errorf("元组表达式%s错误,元组中只有一个成员则必须为度量", tupExp)
		}
	}
	return cube, measure, dimKeys, dimVals, nil
}
func parseMember(memExp string, key string) (string, []string, error) {
	lev, dimName, err := cube.GetLevel(memExp)
	if err != nil {
		return "", nil, err
	}
	keys, err := lev.GetLeafKeys(key)
	if err != nil {
		return "", nil, err
	}
	return dimName, keys, nil
}

//GetNamesByIDs 根据指标ID获取名称
func GetNamesByIDs(indexIds ...string) ([]cube.KeyValue, error) {
	if indexIds == nil || len(indexIds) == 0 {
		return nil, nil
	}
	dMap := make(map[int]cube.KeyValue)
	var children []cube.KeyValue
	var pkv cube.KeyValue
	for _, indexID := range indexIds {
		index, ok := indexMap[indexID]
		if !ok {
			return nil, fmt.Errorf("指标%s不存在", indexID)
		}
		pKey := index.typeID
		if _, ok := dMap[pKey]; !ok {
			pkv = cube.KeyValue{
				Key:   strconv.Itoa(pKey),
				Value: index.typeName,
			}
			children = make([]cube.KeyValue, 0)
		}
		children = append(children, cube.KeyValue{
			Key:   strconv.Itoa(index.id),
			Value: index.name,
		})
		pkv.Children = children
		dMap[pKey] = pkv
	}
	dLen := len(dMap)
	rVals := make([]cube.KeyValue, dLen, dLen)
	k := 0
	for _, v := range dMap {
		rVals[k] = v
		k++
	}
	return rVals, nil
}

//GetIndexMap 获取指标列表
func GetIndexMap() map[string]*Index {
	return indexMap
}

func getValueByExp(exp string, aggType string, context map[string][]string) (*float64, error) {
	indCont, err := parseSet(exp)
	if err != nil {
		return nil, err
	}
	return getIndexContextValue(indCont, aggType, context)
}
func getIndexContextValue(indCont *IndexContext, aggType string, context map[string][]string) (*float64, error) {
	cubeName := indCont.cube
	measure := indCont.measure
	for k := range context {
		if v2, ok := indCont.dimKeys[k]; ok {
			context[k] = v2
		}
	}
	cubeR := cube.GetCubeObject(cubeName)
	val, err := cubeR.Aggregate(context, measure, aggType)
	if err != nil {
		return nil, err
	}
	return val, nil
}

//GetIndexValue 通过ID和Context获取值
func GetIndexValue(indexID string, context map[string][]string) (val *float64, err error) {
	index, ok := indexMap[indexID]
	if !ok {
		return nil, fmt.Errorf("指标%s不存在", indexID)
	}
	if index.context != nil {
		val, err = getIndexContextValue(index.context, index.aggregator, context)
	} else if index.calcExp != nil {
		method := calcMap[index.calcExp.method]
		val, err = method.Do(context, index.aggregator, index.calcExp.exps...)
	}
	if err != nil {
		return nil, err
	}
	return
}

func init() {
	if err := buildIndex(); err != nil {
		panic(err)
	}
}
